package com.biubiushop.socket.netty;

import cn.hutool.log.Log;
import cn.hutool.log.LogFactory;
import com.alibaba.fastjson.JSONObject;
import com.biubiushop.constant.RedisKeyConstant;
import com.biubiushop.entity.Equipment;
import com.biubiushop.entity.Orders;
import com.biubiushop.exception.ServiceException;
import com.biubiushop.repository.IEquipmentRepository;
import com.biubiushop.repository.IOrdersRepository;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import org.springframework.data.redis.core.StringRedisTemplate;

import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;

/**
 * @author chenjia
 * @date 2019/11/5
 **/
public class NettyReadHandlerThread implements Runnable {


    private static final Log log = LogFactory.get();

    private ChannelHandlerContext ctx;

    private String msg;

    private ConcurrentHashMap<String, Channel> socketChannelMap;

    private IEquipmentRepository equipmentRepository;

    private IOrdersRepository ordersRepository;

    private StringRedisTemplate stringRedisTemplate;

    public NettyReadHandlerThread(ChannelHandlerContext ctx, Object msg, ConcurrentHashMap<String, Channel> socketChannelMap, IEquipmentRepository equipmentRepository, IOrdersRepository ordersRepository, StringRedisTemplate stringRedisTemplate) {
        this.ctx = ctx;
        this.msg = msg.toString();
        this.socketChannelMap = socketChannelMap;
        this.equipmentRepository = equipmentRepository;
        this.ordersRepository = ordersRepository;
        this.stringRedisTemplate = stringRedisTemplate;
    }

    /**
     * 根据具体的信息处理相关的业务
     */
    @Override
    public void run() {
        JSONObject jsonObject = stringToJson(msg);

        //是否为下达指令,true-设备出纸指令,false-心跳指令
        boolean flag = jsonObject.get("instructions") != null;
        if (flag) {
            //设备与服务器对应的通讯id
            String socketId = jsonObject.getString("id");
            Channel channel = socketChannelMap.get(socketId);
            String instructions = jsonObject.toJSONString();
            log.info("下达出纸指令给设备");
            replyClient(channel, instructions);
        } else {
            executionInstruction(jsonObject);
        }

    }

    /**
     * 处理指令
     *
     * @param jsonObject
     */
    private void executionInstruction(JSONObject jsonObject) {

        //设备根据指令做业务
        String socketId = jsonObject.getString("id");
        if (null == socketId) {
            //为设备对出纸指令的回应
            //用户支付订单号
            String orderId = jsonObject.getString("orderId");
            //是否出纸成功
            boolean succeed = jsonObject.getBoolean("succeed");
            if (succeed) {
                Orders orders = ordersRepository.getById(orderId);
                orders.setStatus(2);
                ordersRepository.save(orders);
                log.info("订单:" + orderId + "出纸成功");
            } else {
                Orders orders = ordersRepository.getById(orderId);
                orders.setStatus(3);
                ordersRepository.save(orders);
                log.warn("订单:" + orderId + "出纸失败");
            }
        } else {
            Channel channel = ctx.channel();
            //为设备心跳指令
            if (!socketChannelMap.containsKey(socketId)) {
                transformState(jsonObject);
                //客户端第一次发送心跳包
                Equipment equipment = equipmentRepository.getBySocketId(socketId);
                if (null == equipment) {
                    throw new ServiceException("设备信息有误");
                }
                //1.将id与socketId存入Redis
                stringRedisTemplate.opsForValue().set(RedisKeyConstant.getSocketIdKey(equipment.getId()), socketId);
                log.info("设备:" + socketId + "已接入");
            } else {
                //非第一次发送心跳包,检验socketChannel是否相同
                boolean isFirst = socketChannelMap.get(socketId).equals(channel);
                if (!isFirst) {
                    log.info("设备:" + socketId + "对应Channel已变更,设备已进行断线重连");
                }
            }
            //2.将socketid与state(设备状态)存入Redis
            stringRedisTemplate.opsForValue().set(RedisKeyConstant.getEquipmentStateKey(socketId), jsonObject.getString("state"), 120, TimeUnit.SECONDS);

            socketChannelMap.put(socketId, channel);
            replyClient(channel, "get");
            log.info("服务端接收到客户端:" + socketId + "上报设备状态");
        }

    }


    private void transformState(JSONObject jsonObject) {
        String state = jsonObject.getString("state");
        if ("0".equals(state)) {
            jsonObject.put("state", 0);
        } else if ("1".equals(state)) {
            jsonObject.put("state", 1);
        } else if ("10".equals(state)) {
            jsonObject.put("state", 2);
        } else if ("11".equals(state)) {
            jsonObject.put("state", 3);
        } else {
            throw new ServiceException("设备状态错误");
        }
    }

    /**
     * 回复客户端
     */
    private void replyClient(Channel channel, String request) {

        //根据业务返回相应类型
        channel.writeAndFlush(request);
        log.info("服务端向客户端发送信息完毕");
    }

    /**
     * 将设备发过来的数据转为json格式数据
     *
     * @param request
     */
    private static JSONObject stringToJson(String request) {
        JSONObject jsonObject;
        try {
            jsonObject = JSONObject.parseObject(request);
        } catch (Exception e) {
            log.info("数据为非标准json解析格式,数据为:" + request);
            int i = request.indexOf("orderId");
            if (-1 == i) {
                //为设备出纸指令回复
                request = request.replace("orderId", "\"orderId\"");
                request = request.replace("succeed", "\"succeed\"");
            } else {
                //为设备心跳状态包
                request = request.replace("id", "\"id\"");
                request = request.replace("rssi", "\"rssi\"");
                request = request.replace("state", "\"state\"");
            }
            StringBuilder stringBuilder = new StringBuilder(request);
            stringBuilder.insert(0, "{");
            stringBuilder.append("}");
            jsonObject = JSONObject.parseObject(stringBuilder.toString());
        }
        log.info("数据转换为标准json格式数据后为:" + jsonObject);
        return jsonObject;
    }


}
